package com.example.fileupload.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.common.entity.Result;
import com.example.common.exception.NoResultException;
import com.example.common.exception.ServerResponseException;
import com.example.fileupload.entity.FileChunk;
import com.example.fileupload.entity.FileRecord;
import com.example.fileupload.mapper.FileChunkMapper;
import com.example.fileupload.mapper.FileRecordMapper;
import com.example.fileupload.service.FileService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;

/**
 * @author cc
 * @classname FileServiceImpl
 * @description TODO
 * @date 2022/8/14 11:00
 */
@Slf4j
@Service
public class FileServiceImpl implements FileService {

    @Value("${file.save-path}")
    private String savePath;
    @Autowired
    private FileChunkMapper fileChunkMapper;
    @Autowired
    private FileRecordMapper fileRecordMapper;

    /**
     * 获取文件列表
     *
     * @return
     */
    @Override
    public List<FileRecord> getFiles() {
        List<FileRecord> files = fileRecordMapper.getFiles();
        if (CollectionUtils.isEmpty(files)) {
            throw new NoResultException();
        }
        return files;
    }

    /**
     * 检查文件MD5(文件MD5若已存在进行秒传)
     *
     * @param md5
     * @param fileName
     * @return
     */
    @Override
    public Result<Object> checkFileMd5(String md5, String fileName) {
        boolean isParamEmpty = StringUtils.isBlank(md5) || StringUtils.isBlank(fileName) || StringUtils.isBlank(savePath);
        if (isParamEmpty) {
            throw new ServerResponseException("参数值为空");
        }
        //根据MD5和文件名查询分片数据
        List<FileChunk> chunks = fileChunkMapper.getByMd5AndFileName(md5, fileName);
        if (CollectionUtils.isEmpty(chunks)) {
            return Result.ok(null, "文件不存在");
        }
        FileChunk fileChunk = chunks.get(0);
        //第一个条件判断就一个分片，第二个条件判断多个分片是否传输完
        if ((chunks.size() == 1 && fileChunk.getChunks() == 0) || (chunks.size() == fileChunk.getChunks())) {
            return Result.ok(null, "文件已上传成功");
        }

        //全部部分
        List<String> allChunkList = Lists.newArrayList();
        for (int i = 0; i < fileChunk.getChunks(); i++) {
            allChunkList.add(String.valueOf(i));
        }
        //已上传的分片
        List<String> uploadedChunkList = Lists.newArrayListWithCapacity(chunks.size());
        for (FileChunk chunk : chunks) {
            uploadedChunkList.add(String.valueOf(chunk.getChunk()));
        }
        //返回待上传部分
        allChunkList.removeAll(uploadedChunkList);
        JSONArray jsonArray = JSONObject.parseArray(JSONObject.toJSONString(allChunkList));
        return Result.ok(jsonArray, "文件已经上传了一部分");
    }

    /**
     * 分片上传/断点续传
     *
     * @param fileChunk
     * @return
     */
    @Override
    public String chunkUpload(FileChunk fileChunk) {
        try {
            //这里的 chunkSize(分片大小) 要与前端传过来的大小一致
            long chunkSize = 5242880L;
            //定位到该分片的偏移量
            long offset = chunkSize * fileChunk.getChunk();

            RandomAccessFile randomAccessFile = new RandomAccessFile(savePath + File.separator + fileChunk.getName(), "rw");
            randomAccessFile.seek(offset);
            randomAccessFile.write(fileChunk.getFile().getBytes());
            randomAccessFile.close();

            //保存分片上传信息
            fileChunk.setUid(fileChunk.getUid() + "_" + fileChunk.getChunk());
            fileChunk.setCreateTime(new Date());
            fileChunk.setUpdateTime(new Date());
            fileChunkMapper.saveFileChunk(fileChunk);
        } catch (IOException e) {
            log.error("文件上传失败：" + e);
            throw new ServerResponseException("文件上传失败");
        }
        return "文件上传成功";
    }

    /**
     * 断点续传完成后上传文件信息进行入库操作
     *
     * @param fileRecord
     * @return
     */
    @Override
    public String saveFile(FileRecord fileRecord) {
        //通过文件名查询数量
        Integer count = fileRecordMapper.getCountByFileName(fileRecord.getFileName());
        if (count > 0) {
            return "文件信息已存在";
        }
        fileRecord.setCreateTime(new Date());
        fileRecord.setUpdateTime(new Date());
        fileRecordMapper.saveFile(fileRecord);
        return "断点续传完成文件信息入库成功";
    }

    /**
     * 文件下载
     *
     * @param request
     * @param response
     * @param id
     * @return
     */
    @Override
    public String download(HttpServletRequest request, HttpServletResponse response, Long id) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            FileRecord fileRecord = fileRecordMapper.getFile(id);
            inputStream = Files.newInputStream(Paths.get(savePath + File.separator + fileRecord.getFileName()));
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileRecord.getFileName(), "UTF-8"));
            //获取输出流
            outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            log.error("文件下载失败：" + e);
            throw new ServerResponseException("文件下载失败");
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "文件下载成功";
    }
}
